#include <tsin/library/BulletGenerator.hpp>

#include <tsin/library/BulletDirector.hpp>
#include <tsin/library/BulletPlacer.hpp>
#include <tsin/library/BulletRouter.hpp>
#include <tsin/library/BulletDresser.hpp>
#include <tsin/library/BulletEventFiller.hpp>
#include <tsin/library/Game.hpp>
#include <tsin/library/GameViewport.hpp>
#include <tsin/runtime/BulletRuntime.hpp>
#include <tsin/runtime/GameRuntime.hpp>
#include <tsin/util/Set.hpp>
#include <tsin/util/Logger.hpp>

#include <cstring>

#include <tsin/util/MemCheck.hpp>
using namespace tsin;

BulletGenerator::BulletGenerator()
        : defaultNum(0),
           placer(0), director(0), router(0), dresser(0), filler(0)
{
}

BulletGenerator::~BulletGenerator()
{
        if (placer)
                delete placer;
        if (director)
                delete director;
        if (router)
                delete router;
        if (dresser)
                delete dresser;
}

DynamicObject *BulletGenerator::copy()
{
        return DynamicObject::copy(new BulletGenerator);
}

void BulletGenerator::init(int num, BulletDresser *dresser, BulletPlacer *placer,
                           BulletDirector *director, BulletRouter *router,
                           BulletEventFiller *filler)
{
        this->defaultNum = num;
        this->dresser = dresser;
        this->placer = placer;
        this->director = director;
        this->router = router;
        this->filler = filler;
}

void BulletGenerator::reinit()
{
        int                arg1 = arguments[0]->getInt();
        BulletDresser     *arg2 = static_cast<BulletDresser *>(arguments[1]);
        BulletPlacer      *arg3 = static_cast<BulletPlacer *>(arguments[2]);
        BulletDirector    *arg4 = static_cast<BulletDirector *>(arguments[3]);
        BulletRouter      *arg5 = static_cast<BulletRouter *>(arguments[4]);
        BulletEventFiller *arg6 = static_cast<BulletEventFiller *>(arguments[5]);

        switch(nrArguments) {
        case 6:
                if (strcmp(desp, "NPPPPP") == 0)
                        init(arg1, arg2, arg3, arg4, arg5, arg6);
                break;
        }
}

DynamicEvent BulletGenerator::postTick(void *data, void *)
{
        generate(currentGame, currentGame->game->getViewport(0), data);
        return EVENT_NONE;
}

char BulletGenerator::getDescriptor()
{
        return 'P';
}

void BulletGenerator::generate(GameRuntime *runtime, GameViewport *viewport, void *data)
{
        generate(runtime, viewport, data, defaultNum);
}

void BulletGenerator::generate(GameRuntime *runtime, GameViewport *viewport, void *data, int n)
{
        if (placer == 0 || director == 0 || dresser == 0 || filler == 0)
                return;

        for (int i = 0; i < nrArguments; i++)
                arguments[i]->preTick(data);

        Set<BulletRuntime *> *bullets = runtime->enemy_bullets;
        placer->reset();
        director->reset();
        router->reset();
        dresser->reset();
        filler->reset();

        for (int i = 0; i < n; i ++) {
                BulletRuntime *bullet = new BulletRuntime;
                bullet->residence = viewport;
                bullet->stage = BULLET_STAGE_BEFALL;
                bullet->z = zNear;
                bullet->align = Display::HORIZONTAL_CENTER | Display::VERTICAL_CENTER;

                placer->place(bullet, data);
                director->direct(bullet, data);
                if (router)
                        router->route(bullet);
                dresser->dress(bullet);
                filler->fill(bullet);

                if (!bullets->insert(bullet)) {
                        delete bullet;
                        return;
                }
        }

        for (int i = 0; i < nrArguments; i++)
                arguments[i]->postTick(data);
        reinit();
}

extern "C" LIBRARY_DLLEXPORT DynamicObject *bindGenerator()
{
        return new BulletGenerator;
}
